﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xeralabs.Core.Entities;
using Xeralabs.Core.Events;
using Xeralabs.Core.Kernel;

namespace Xeralabs.Core
{
    public sealed class XGDESK
    {
        public struct InitParams
        {
            public static InitParams Default =
#if DEBUG
 new InitParams()
 {
     SimulationFrameRate = 60,
     KernelMessagePoolFullInit = false,
     KernelMessagePoolMaxSize = 100,
     KernelMessagePoolWarmupSize = 25,
     Forced = false
 };
#else
new InitParams()
{
     SimulationFrameRate = 60,
     KernelMessagePoolFullInit = false,
     KernelMessagePoolMaxSize = 1000,
     KernelMessagePoolWarmupSize = 500,
     Forced = false
 };
#endif

            public int SimulationFrameRate { get; set; }

            public int KernelMessagePoolMaxSize { get; set; }
            public bool KernelMessagePoolFullInit { get; set; }
            public int KernelMessagePoolWarmupSize { get; set; }

            public bool Forced { get; set; }
        }

        #region Static Class (Singleton)
        private static XGDESK _instance;

        public static XGDESK GetInstance()
        {
            if (_instance == null)
                throw new NotInitializedException();
            else
                return _instance;
        }

        public static bool Initialize()
        {
            return Initialize(InitParams.Default);
        }

        public static bool Initialize(InitParams initParams)
        {
            if (_instance == null)
            {
                _instance = new XGDESK(initParams);
            }
            else if (initParams.Forced)
            {
                _instance.ForcedDispose();
                _instance = null;
                _instance = new XGDESK(initParams);
            }
            return (_instance != null);
        }

        public static void AddEntity(GDESKEntity entity)
        {
            EntityManager.GetSingleton().AddEntity(entity);
        }

        public static void Start()
        {
            if (_instance == null)
                throw new NotInitializedException();


            if (!_instance._running)
            {
                _instance.Run();
            }
        }

        public static void End()
        {
            if (_instance == null)
                throw new NotInitializedException();

            if (_instance._running)
            {
                GDESKEvent item = _instance._eventPool.GetObject();
                item.Input(EntityManager.SingletonGUID, EntityManager.SingletonGUID, 0.0, (byte)(Priority.Kernel | Priority.High), new EndMessage());
                _instance._dispatcher.EnqueueEvent(item);
                _instance._ending = true;
            }
            else
            {
                KillInstance();
            }
        }

        internal static void Ended()
        {
            if (_instance._ending)
            {
                KillInstance();
            }
        }

        private static void KillInstance()
        {
            _instance.Stop();
            _instance = null;
        }

        private XGDESK(InitParams initParams)
        {
            _initParams = initParams;
            InitializeSelf();
        }
        #endregion

        private InitParams _initParams;

        private EventPool _eventPool;
        private Dispatcher _dispatcher;
        private bool _ending;
        private bool _running;

        public FrameEventHandler Tick { set { _dispatcher.Tick += value; } }
        public FrameEventHandler FrameEnd { set { _dispatcher.FrameEnd += value; } }

        private void InitializeSelf()
        {
            try
            {
                if (EntityManager.Initialize())
                {
                    if (_initParams.KernelMessagePoolFullInit)
                        _eventPool = new EventPool(_initParams.KernelMessagePoolMaxSize, _initParams.KernelMessagePoolFullInit);
                    else
                        _eventPool = new EventPool(_initParams.KernelMessagePoolMaxSize, _initParams.KernelMessagePoolWarmupSize);

                    _dispatcher = new Dispatcher(_initParams.SimulationFrameRate);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        private void Run()
        {
            if (!_running)
            {
                _dispatcher.Start();
                _running = true;
            }
        }

        private void Stop()
        {
            _dispatcher.Recycle(false);
            //Do Forced Dispose when signaled
        }

        private void ForcedDispose()
        {
            _dispatcher.Recycle(true);
            _dispatcher.Stop();
            _dispatcher.Dispose();
            _dispatcher = null;

            _eventPool.Dispose();
            _eventPool = null;
        }

        internal GDESKEvent GetEvent()
        {
            return _eventPool.GetObject();
        }

        internal void PushEvent(GDESKEvent e)
        {
            _dispatcher.EnqueueEvent(e);
        }

        internal void RecycleEvent(GDESKEvent e)
        {
            if (e != null)
            {
                _eventPool.ReleaseObject(e);
                e = null;
            }
        }
    }
}